import numpy
import sys,os,inspect
from PyQt4 import QtCore
from KTrade.KStockFeed import KStockFeed
from KModel.KSingleStock import KSingleStock
from KIndicators import KBaseIndicator
from KCharts.KGraphics.KGraphicsSceneSubViewModel import KGraphicsSceneSubViewModel
from KCharts.KGraphics.KBaseDisplayItem import KBaseDisplayItem
from KApp import KConstants as vm
from PyQt4.QtGui import QGraphicsObject,QPen,QBrush,QColor,QPolygonF
from PyQt4 import QtCore,QtGui,Qt
from PyQt4.QtCore import QPointF
import numpy as np
class KAtomPeak(KBaseIndicator.KBaseIndicator):
	''' the atomic indicator to search for peaks among the stock prices'''
	def __init__(self,parent=None):
		super(KAtomPeak,self).__init__(parent)
		self.windowsize=4
		self.logicheightratio=0.05
		self.logicoffsetratio=0.3
		
	def GetPeakList(self):
		return self.peaklist
	
	def GetPeakCount(self):
		return len(self.peaklist)
	
	def GetWindowSize(self):
		return self.windowsize
	def SetWindowSize(self,newsize):
		self.windowsize=newsize
	
	def GetPeakLogicHeightAndOffset(self):
		# get the logic height , y axis offset of the peak triangle
		(pxrange,pyrange)=self.m_stock.CurWindowRange('Price')	
		logicheight=(pyrange[1]-pyrange[0])*self.logicheightratio
		logicoffset=self.logicoffsetratio*logicheight
		return (logicheight,logicoffset)
		
	def GetLogicRange(self):
		# get the range of all data in this indicator
		if self.peaklist:
			(pxrange,pyrange)=self.m_stock.CurWindowRange('Price')	

			windowpeaklist=[]
			[windowpeaklist.append(each_peak) for each_peak in self.peaklist if each_peak[0]>pxrange[0] and each_peak[0]<pxrange[1]]
			if len(windowpeaklist)>0:
				npeaklist=np.array(windowpeaklist)		
				ydatarange=[np.min(npeaklist[:,1]),np.max(npeaklist[:,1])]
				xdatarange=[np.min(npeaklist[:,0]),np.max(npeaklist[:,0])]	
				# extend yrange to include peak triangel height and offset
				(lheight,loffset)=self.GetPeakLogicHeightAndOffset()
				ylength=abs(ydatarange[0]-ydatarange[1])
				ydatarange[0]-=(lheight+loffset)
				ydatarange[1]+=(lheight+loffset)
				return (xdatarange,ydatarange)
		return self.m_stock.CurWindowRange('Price')
	
	def calculate(self,**kargs):		
		
		
		#self.peaklist=KAtomPeak.FindPeaks(self.m_stock.GetNumpyArray(),self.windowsize)

		self.OnStart()
		self.stockfeed=KStockFeed(self.m_stock)
		stockfeed=self.stockfeed
		while stockfeed.HasNextBar():
			stockfeed.MoveForward()
			self.OnNewBars(stockfeed.GetCurrentIndex())		
		self.OnFinish()
		
		return self.peaklist


	@staticmethod
	def FindPeaks(npricearry,windowsize):
	# find the peaks in the price array, remove weak extremas, deprecated, use onNewBars instead
		highprice=npricearry[:,1]
		lowprice=npricearry[:,2]		
		templist=[]
		
		# find intial extrema
		for each_index in range(1,len(highprice)-1):
			if highprice[each_index]>highprice[each_index-1] and highprice[each_index]>highprice[each_index+1]:
				templist.append([each_index,highprice[each_index],1]) # 1 denote maxima
			if lowprice[each_index]<lowprice[each_index-1] and lowprice[each_index]<lowprice[each_index+1]:
				templist.append([each_index,lowprice[each_index],-1])	# -1 denote minima		
		
		# filter out 'weak' extrema: those have a stronger extrema as its neighbor within a window will be discarded
		peaklist=[]
		for pindex in range(0,len(templist)):
			xindex=templist[pindex][0]
			noadd=False
			leftlimit=max(xindex-windowsize,0)
			rightlimit=min(xindex+windowsize,len(highprice))
			if templist[pindex][2]<0: # remove weak minima
				nearbymin=min(lowprice[leftlimit:rightlimit])
				if nearbymin<templist[pindex][1]:
					noadd=True
			else:# remove weak maxima
				nearbymax=max(highprice[leftlimit:rightlimit])
				if nearbymax>templist[pindex][1]:
					noadd=True	
			if not noadd:
				peaklist.append(templist[pindex])
				
		# each peak elem is in term of [index pos, peak price, flag] 	
		return peaklist
	
	## online functions
	def OnNewBars(self,curfeedindex):
		# used in online mode, will be called when a new bar is available
		

		leftindex=curfeedindex-self.windowsize*2 # consider the index windowsize bar ago

		if leftindex<0:
			return
		considerindex=leftindex+self.windowsize
		
		highprice=self.stockfeed.GetBarWindowByIndex('high',[leftindex,curfeedindex+1])
		lowprice=self.stockfeed.GetBarWindowByIndex('low',[leftindex,curfeedindex+1])
		
		curpos=self.windowsize
		
		 
		if not np.max(highprice)>highprice[curpos]:
			# denote a max extrema
			self.peaklist.append([considerindex,highprice[curpos],1])
		if not np.min(lowprice)<lowprice[curpos]:
			self.peaklist.append([considerindex,lowprice[curpos],-1])
			
		

		
	def OnStart(self):
		# used in online mode, called before simulation started
		self.peaklist=[]
		
	def OnFinish(self):
		# used in online mode, called after simulation exectued
		KBaseIndicator.KBaseIndicator.update(self)# update data after calcuation
	
class KAtomPeakDisplayItem(KBaseIndicator.KBaseIndicatorDisplayItem):
	

	
	def PaintPeak(self,painter):
		
	
			
		# start painting		

		mpen=QPen(QColor(255,255,0))
		mpen.setStyle(QtCore.Qt.DashLine)
		mpen.setWidthF(1.5)		
		#painter.setPen(mpen)

		width=1.0/2.0
		logicheight,logicoffset=self.model.GetPeakLogicHeightAndOffset()
		[width],[height,peakoffset]=self.viewmodel.mapLogiceSizeToScene(vm.RectType.ContentBound,[width],[logicheight,logicoffset])
	
	
		
		
		# only show extrema in current window
		XCordinates=self.m_stock.CurWindowData('Index')
		minx=min(XCordinates)
		maxx=max(XCordinates)
		peakblocks=self.model.GetPeakList()
		peakwindows=[x for x in peakblocks if x[0]>minx and x[0]<maxx]
		
		for index in range(0,len(peakwindows)):
			xcord,yprice,flag=peakwindows[index]
			([xcord],[ycord])=self.viewmodel.mapLogiceCordToScene(vm.RectType.ContentBound,[xcord],[yprice])


			painter.setBrush(QBrush(QColor(255,255,0)))

			if flag<0:
				pointlist=QPolygonF([QPointF(xcord-width,ycord+peakoffset),QPointF(xcord,ycord+peakoffset+height),QPointF(xcord+width,ycord+peakoffset)])
			else:
				pointlist=QPolygonF([QPointF(xcord-width,ycord-peakoffset),QPointF(xcord,ycord-peakoffset-height),QPointF(xcord+width,ycord-peakoffset)])
			painter.drawPolygon(pointlist)
			
		
	def PrepareRenderPicture(self):
		## pre-computing a QPicture object allows paint() to run much more quickly, 
		## rather than re-drawing the shapes every time.

		if not hasattr(self,'model') :
				return
		if not self.model.IsReady():
			return 		
		if self.model.IsResultExpired()==True:
			self.model.calculate()	
				
		self.picture = QtGui.QPicture()
		painter = QtGui.QPainter(self.picture)	

		self.PaintPeak(painter)

		painter.end()	
